// Kony

/**
 * 这是能力系统组件的基类 .app
 */
#include "AbilitySystem/BaseAbilitySystemComponent.h"

#include "AbilitySystemBlueprintLibrary.h"
#include "BaseGameplayTags.h"
#include "AbilitySystem/BaseAbilitySystemLibrary.h"
#include "AbilitySystem/Abilitys/BaseGameplayAbility.h"
#include "AbilitySystem/Data/AbilityInfo.h"
#include "Game/LoadScreenSaveGame.h"
#include "GASC_HuanLing/BaseLogChannels.h"
#include "Interaction/PlayerInterface.h"

void UBaseAbilitySystemComponent::AbilityActorInfoSet()
{
	// 当能力角色的信息设置完成时调用此方法,此时可以执行EffectApplied等相关操作
	// 将EffectApplied方法注册到OnGameplayEffectAppliedDelegateToSelf 委托中
	// 当服务器端对自身应用游戏效果(GE)时触发，包括瞬时效果和持续效果。
	OnGameplayEffectAppliedDelegateToSelf.AddUObject(this,&UBaseAbilitySystemComponent::ClientEffectApplied);

	
	
	//获取C++创建的游戏标签单例 
	// const FBaseGameplayTags& GameplayTags = FBaseGameplayTags::Get();
	// //打印日志测试读取游戏标签 这部分是用于测试 后期不需要了可删除
	// GEngine ->AddOnScreenDebugMessage(-1, 10.f, FColor::Orange,
	// 	FString::Printf(TEXT("Attributes_Secondary_Armor:%s"),*GameplayTags.Attributes_Secondary_Armor.ToString()));
	
 
}

void UBaseAbilitySystemComponent::AddCharacterAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupAbilities)
{
	//遍历传入的数组，逐个添加能力
	for (TSubclassOf<UGameplayAbility> AbilityClass : StartupAbilities)
	{
		//获取能力规格
		FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass,1);
		if (const UBaseGameplayAbility* BaseAbility = Cast<UBaseGameplayAbility>(AbilitySpec.Ability))
		{
			//获取启动输入标签并添加到AbilitySpec中
			AbilitySpec.DynamicAbilityTags.AddTag(BaseAbility->StartupInputTag);
			//设置能力已装备标签 Abilities_Status_Equipped
			AbilitySpec.DynamicAbilityTags.AddTag(FBaseGameplayTags::Get().Abilities_Status_Equipped);
			//授予能力
			GiveAbility(AbilitySpec);
			//授予并激活一次 这个不允许使用常量
			// GiveAbilityAndActivateOnce(AbilitySpec);
		}
	}
	//执行到这里时把启动能力标记为true，避免还没有赋予就执行回调
	bStartupAbilitiesGiven = true;
	//能力赋予委托,在能力被赋予时广播这个委托,用于给技能栏装备技能
	AbilitiesGivenDelegate.Broadcast();
}
//添加角色 被动启动能力,被动能力只激活一次
void UBaseAbilitySystemComponent::AddCharacterPassiveAbilities(
	const TArray<TSubclassOf<UGameplayAbility>>& StartupPassiveAbilities)
{
	//遍历传入的数组，逐个添加能力
	for (TSubclassOf<UGameplayAbility> AbilityClass : StartupPassiveAbilities)
	{
		//获取能力规格
		FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass,1);
		//给被动技能增加已装备标签 用于加载存档时读取
		AbilitySpec.DynamicAbilityTags.AddTag(FBaseGameplayTags::Get().Abilities_Status_Equipped);
		//被动能力只激活一次
		GiveAbilityAndActivateOnce(AbilitySpec);
	}
}
//从存档中读取并添加角色能力
void UBaseAbilitySystemComponent::AddCharacterAbilitiesFromSaveData(ULoadScreenSaveGame* SaveData)
{
	//循环存档中保存的能力结构体数组
	for (const FSavedAbility& Data : SaveData->SavedAbilities)
	{
		//取出加载能力
		const TSubclassOf<UGameplayAbility> LoadedAbilityClass = Data.GameplayAbility;
		//从能力取出能力规格
		FGameplayAbilitySpec LoadedAbilitySpec = FGameplayAbilitySpec(LoadedAbilityClass,Data.AbilityLevel);
		//添加能力输入标签
		LoadedAbilitySpec.DynamicAbilityTags.AddTag(Data.AbilityInputTag);
		//添加能力状态标签
		LoadedAbilitySpec.DynamicAbilityTags.AddTag(Data.AbilityStatusTag);
		//判断能力是主动还是被动
		if (Data.AbilityTypeTag == FBaseGameplayTags::Get().Abilities_Type_Offensive)
		{
			//主动技能 赋予能力
			GiveAbility(LoadedAbilitySpec);
		}
		else if (Data.AbilityTypeTag == FBaseGameplayTags::Get().Abilities_Type_Passive)
		{
			//判断技能是否已装备，只有已装备的被动才激活
			if (Data.AbilityStatusTag.MatchesTagExact(FBaseGameplayTags::Get().Abilities_Status_Equipped))
			{
				//被动技能 赋予能力并直接激活
				GiveAbilityAndActivateOnce(LoadedAbilitySpec);
			}else
			{
				//如果没装备则仅赋予能力 不直接激活
				GiveAbility(LoadedAbilitySpec);
			}
		}
	}
	//执行到这里时把启动能力标记为true，避免还没有赋予就执行回调
	bStartupAbilitiesGiven = true;
	//能力赋予委托,在能力被赋予时广播这个委托,用于给技能栏装备技能
	AbilitiesGivenDelegate.Broadcast();
}

//处理按键按下函数,根据标签执行对应能力
void UBaseAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& InputTag)
{
	if (!InputTag.IsValid()) return;
	//能力范围锁，防止在遍历过程中修改列表 导致能力变化
	FScopedAbilityListLock ActiveSpecsLock(*this);
	//遍历可激活能力
	for (FGameplayAbilitySpec& AbilitySpec:GetActivatableAbilities())
	{
		//匹配动态能力标签
		if (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag))
		{
			//通知能力系统被按下
			AbilitySpecInputPressed(AbilitySpec);
			//激活能力
			if (AbilitySpec.IsActive())
			{
				//告知服务器我们正在执行InputPressed操作 （用于持续技能 能力系统的<等待输入释放功能>）
				InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, AbilitySpec.Handle, AbilitySpec.ActivationInfo.GetActivationPredictionKey());
			}
		}
	}
}
//处理按键释放函数,根据标签执行对应能力
void UBaseAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& InputTag)
{
	if (!InputTag.IsValid()) return;
	//能力范围锁，防止在遍历过程中修改列表 导致能力变化
	FScopedAbilityListLock ActiveSpecsLock(*this);
	//遍历可激活能力
	for (FGameplayAbilitySpec& AbilitySpec:GetActivatableAbilities())
	{
		//匹配动态能力标签 并检测 能力是否激活
		if (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag) && AbilitySpec.IsActive())
		{
			//通知能力系统被释放
			AbilitySpecInputReleased(AbilitySpec);
			//告知服务器我们正在执行InputReleased操作 （用于持续技能 能力系统的<等待输入释放功能>）
			InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, AbilitySpec.Handle, AbilitySpec.ActivationInfo.GetActivationPredictionKey());
		}
	}
}
//处理按键持续按住函数,根据标签执行对应能力
void UBaseAbilitySystemComponent::AbilityInputTagHeld(const FGameplayTag& InputTag)
{
	if (!InputTag.IsValid()) return;
	//能力范围锁，防止在遍历过程中修改列表 导致能力变化
	FScopedAbilityListLock ActiveSpecsLock(*this);
	//遍历可激活能力
	for (FGameplayAbilitySpec& AbilitySpec:GetActivatableAbilities())
	{
		//匹配动态能力标签
		if (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag))
		{
			//通知能力系统被按下
			AbilitySpecInputPressed(AbilitySpec);
			//激活能力
			if (!AbilitySpec.IsActive())
			{
				//如果没激活就激活能力
				TryActivateAbility(AbilitySpec.Handle);
			}
		}
	}
}
//遍历所有已激活的能力
void UBaseAbilitySystemComponent::ForEachAbility(const FForEachAbility Delegate)
{
	//能力范围锁，防止在遍历过程中修改列表 导致能力变化
	FScopedAbilityListLock ActiveSpecsLock(*this);
	for (const FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
	{
		//调用委托 回调函数
		if (!Delegate.ExecuteIfBound(AbilitySpec))
		{
			//如果没绑定回调函数就输出个日志
			UE_LOG(LogHuanYue, Error, TEXT("Failed to execute delegate in %hs"), __FUNCDNAME__);
		}
	}
}
//获取游戏能力规格包含的能力标签
FGameplayTag UBaseAbilitySystemComponent::GetAbilityTagFromSpec(const FGameplayAbilitySpec AbilitySpec)
{
	if (AbilitySpec.Ability)
	{
		for (FGameplayTag Tag : AbilitySpec.Ability.Get()->AbilityTags)
		{
			//判断标签是否是能力标签
			if (Tag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("Abilities"))))
			{
				return Tag;
			}
		}
	}
	return FGameplayTag();
}
//获取游戏能力规格包含的输入按键标签
FGameplayTag UBaseAbilitySystemComponent::GetInputTagFromSpec(const FGameplayAbilitySpec AbilitySpec)
{
	for (FGameplayTag Tag : AbilitySpec.DynamicAbilityTags)
	{
		//判断标签是否是能力标签
		if (Tag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("InputTag"))))
		{
			return Tag;
		}
	}
	return FGameplayTag();
}
//获取能力状态标签
FGameplayTag UBaseAbilitySystemComponent::GetStatusFromSpec(const FGameplayAbilitySpec& AbilitySpec)
{
	//获取所有动态标签
	for (FGameplayTag StatusTag : AbilitySpec.DynamicAbilityTags)
	{
		//匹配包含 Abilities.Status的 状态标签，返回状态标签
		if (StatusTag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("Abilities.Status"))))
		{
			return StatusTag;
		}
	}
	//返回空
	return FGameplayTag();
}
//获取能力状态标签,这是一个动态的 通过能力标签获取能力状态的函数 和 上面的有一些不同
FGameplayTag UBaseAbilitySystemComponent::GetStatusTagFromAbilityTag(const FGameplayTag& AbilityTag)
{
	//通过能力标签获取能力规格
	if (const FGameplayAbilitySpec* Spec = GetSpecFromAbilityTag(AbilityTag))
	{
		//返回状态标签
		return GetStatusFromSpec(*Spec);
	}
	return FGameplayTag();
}
//获取输入标签，这是一个动态的 通过能力标签获取输入标签的函数
FGameplayTag UBaseAbilitySystemComponent::GetSlotFromAbilityTag(const FGameplayTag& AbilityTag)
{
	//通过能力标签获取能力规格
	if (const FGameplayAbilitySpec* Spec = GetSpecFromAbilityTag(AbilityTag))
	{
		//输入输入标签
		return GetInputTagFromSpec(*Spec);
	}
	return FGameplayTag();
}
//返回插槽是否为空
bool UBaseAbilitySystemComponent::SlotIsEmpty(const FGameplayTag& Slot)
{
	//作用域能力列表锁 避免遍历时被修改或删除
	FScopedAbilityListLock AbilityScopeLock(*this);
	//循环已激活能力
	for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
	{
		//返回检查槽位结果，如果为不为空就返回false
		if (AbilityHasSlot(AbilitySpec,Slot)) return false;
	}
	return true;
}
//能力规格中调用 槽位检查返回结果
bool UBaseAbilitySystemComponent::AbilityHasSlot(const FGameplayAbilitySpec& Spec, const FGameplayTag& Slot)
{
	//返回检查槽位结果
	return Spec.DynamicAbilityTags.HasTagExact(Slot);
}
//判断能力是否拥有任何一个槽位
bool UBaseAbilitySystemComponent::AbilityHasAnySlot(const FGameplayAbilitySpec& Spec)
{
	//返回检查槽位是否拥有任何输入标签
	return Spec.DynamicAbilityTags.HasTag(FGameplayTag::RequestGameplayTag(FName("InputTag")));
}

//判断是否是被动能力
bool UBaseAbilitySystemComponent::IsPassiveAbility(const FGameplayAbilitySpec& Spec) const
{
	//获取能力信息
	const UAbilityInfo* AbilityInfo = UBaseAbilitySystemLibrary::GetAbilityInfo(GetAvatarActor());
	//获取能力标签
	const FGameplayTag AbilityTag = GetAbilityTagFromSpec(Spec);
	//从能力信息中获取能力标签
	const FBaseAbilityInfo Info = AbilityInfo->FindAbilityInfoForTag(AbilityTag);
	const FGameplayTag AbilityType = Info.TypeTag;
	//匹配类型标签是否是被动类型
	return AbilityType.MatchesTag(FBaseGameplayTags::Get().Abilities_Type_Passive);
}
//为能力分配槽位
void UBaseAbilitySystemComponent::AssignSlotToAbility(FGameplayAbilitySpec& Spec, const FGameplayTag& Slot)
{
	//清除当前槽位
	ClearSlot(&Spec);
	//分配新技能到这个槽位，也就是把这个槽位的输入标签分配到新技能能力上
	Spec.DynamicAbilityTags.AddTag(Slot);
}

//获取该插槽能力规格
FGameplayAbilitySpec* UBaseAbilitySystemComponent::GetSpecWithSlot(const FGameplayTag& Slot)
{
	//作用域能力列表锁 避免遍历时被修改或删除
	FScopedAbilityListLock AbilityScopeLock(*this);
	//循环可激活能力规格列表
	for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
	{
		//如果技能规格中包含该插槽标签，则返回能力规格
		if (AbilitySpec.DynamicAbilityTags.HasTagExact(Slot))
		{
			return &AbilitySpec;
		}
	}
	return nullptr;
}

//获取对应技能能力标签的游戏能力规格
FGameplayAbilitySpec* UBaseAbilitySystemComponent::GetSpecFromAbilityTag(const FGameplayTag& AbilityTag)
{
	//作用域能力列表锁 避免遍历时被修改或删除
	FScopedAbilityListLock AbilityScopeLock(*this);
	//循环可激活能力规格列表
	for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
	{
		//循环能力规格里存在的所有能力标签
		for (FGameplayTag Tag : AbilitySpec.Ability.Get()->AbilityTags)
		{
			//判断标签是否匹配
			if (Tag.MatchesTag(AbilityTag))
			{
				//返回游戏能力规格
				return &AbilitySpec;
			}
		}
	}
	return nullptr;
}

//升级属性函数
void UBaseAbilitySystemComponent::UpgradeAttribute(const FGameplayTag& AttributeTag)
{
	//判断演员是否包含玩家接口类
	if (GetAvatarActor()->Implements<UPlayerInterface>())
	{
		//如果当前可用属性点大于0
		if (IPlayerInterface::Execute_GetAttributePoints(GetAvatarActor()) > 0)
		{
			//通知服务器端升级属性
			ServerUpgradeAttribute(AttributeTag);
		}
	}
}
//服务端执行给玩家属性升级
void UBaseAbilitySystemComponent::ServerUpgradeAttribute_Implementation(const FGameplayTag& AttributeTag)
{
	//设置游戏事件数据载荷，用于发送给处理游戏能力执行事件监听位置 处理升级属性
	FGameplayEventData Payload;
	Payload.EventTag = AttributeTag; //游戏标签
	Payload.EventMagnitude = 1.f; //游戏事件幅度，也就是提升多少值
	//发送游戏事件
	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetAvatarActor(), AttributeTag, Payload);

	
	if (GetAvatarActor()->Implements<UPlayerInterface>())
	{
		//减少可用属性点
		IPlayerInterface::Execute_AddToAttributePoints(GetAvatarActor(), -1);
	}
}
//根据等级查询并更新能力信息里是否有复合当前等级解锁的技能
void UBaseAbilitySystemComponent::UpdateAbilityStatuses(int32 Level)
{
	UAbilityInfo* AbilityInfo = UBaseAbilitySystemLibrary::GetAbilityInfo(GetAvatarActor());
	//遍历所有能力信息
	for (const FBaseAbilityInfo& Info : AbilityInfo->AbilityInformation)
	{
		//判断能力标签不为空
		if (!Info.AbilityTag.IsValid()) continue;
		//检查等级需求是否满足,不满足直接返回
		if (Level < Info.LevelRequirement) continue;
		//获取对应能力标签的技能
		if (GetSpecFromAbilityTag(Info.AbilityTag) == nullptr)
		{
			//创建能力
			FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(Info.AbilityClass,1);
			//设置动态能力标签，添加符合条件可激活标签
			AbilitySpec.DynamicAbilityTags.AddTag(FBaseGameplayTags::Get().Abilities_Status_Eligible);
			FGameplayTag InputTag = GetInputTagFromSpec(AbilitySpec);
			//赋予能力
			GiveAbility(AbilitySpec);
			//这个是立即执行的能力，所以这里要标记
			MarkAbilitySpecDirty(AbilitySpec);
			//发送给客户端更新能力广播
			ClientUpdateAbilityStatus(Info.AbilityTag, InputTag, FBaseGameplayTags::Get().Abilities_Status_Eligible,1);
		}
	}
}
//服务器执行的升级技能处理，进行RPC 复制给客户端
void UBaseAbilitySystemComponent::ServerSpendSpellPoint_Implementation(const FGameplayTag& AbilityTag)
{
	//判断技能是否处于已激活的能力
	if (FGameplayAbilitySpec* AbilitySpec = GetSpecFromAbilityTag(AbilityTag))
	{
		//判断化身角色 是否实现了玩家接口,判断时要用U开头 ，调用时候要用 I开头
		if (GetAvatarActor()->Implements<UPlayerInterface>())
		{
			//让玩家的技能点减少1，消耗一个技能点
			IPlayerInterface::Execute_AddToSpellPoints(GetAvatarActor(), -1);
		}
		const FBaseGameplayTags GameplayTags = FBaseGameplayTags::Get();
		//从动态标签中获取状态标签
		FGameplayTag Status = GetStatusFromSpec(*AbilitySpec);
		FGameplayTag InputTag = GetInputTagFromSpec(*AbilitySpec);
		//判断是否处于 可激活状态，消耗技能点解锁技能
		if (Status.MatchesTagExact(GameplayTags.Abilities_Status_Eligible))
		{
			//先移除可解锁状态
			AbilitySpec->DynamicAbilityTags.RemoveTag(GameplayTags.Abilities_Status_Eligible);
			//再赋予已解锁状态
			AbilitySpec->DynamicAbilityTags.AddTag(GameplayTags.Abilities_Status_Unlocked);
			Status = GameplayTags.Abilities_Status_Unlocked;
		}
		//判断是已装备 或 已解锁 消耗技能点提升技能等级
		else if (Status.MatchesTagExact(GameplayTags.Abilities_Status_Equipped) || Status.MatchesTagExact(GameplayTags.Abilities_Status_Unlocked) )
		{
			//给技能升级
			AbilitySpec->Level += 1;
		}
		//广播技能状态更新到所有客户端
		ClientUpdateAbilityStatus(AbilityTag, InputTag, Status, AbilitySpec->Level);
		//强制更新技能 即时生效
		MarkAbilitySpecDirty(*AbilitySpec);
	}
}
//服务器处理技能（拖拽或指定）分配装备到对应的技能栏插槽
void UBaseAbilitySystemComponent::ServerEquipAbility_Implementation(const FGameplayTag& AbilityTag, const FGameplayTag& Slot)
{
	//判断当前选择的技能 是否处于 已激活的能力中，如果不存则 则不能装备技能
	if (FGameplayAbilitySpec* AbilitySpec = GetSpecFromAbilityTag(AbilityTag))
	{
		const FBaseGameplayTags GameplayTags = FBaseGameplayTags::Get();
		//获取输入标签
		const FGameplayTag& PrevSlot = GetInputTagFromSpec(*AbilitySpec);
		//获取状态标签
		const FGameplayTag& Status = GetStatusFromSpec(*AbilitySpec);
		//判断只有状态为已装备或已解锁的技能才能装备
		const bool bStatusValid = Status == GameplayTags.Abilities_Status_Equipped || Status == GameplayTags.Abilities_Status_Unlocked;
		if (bStatusValid)
		{
			//处理被动技能
			//判断槽位不等于空
			if (!SlotIsEmpty(Slot))
			{
				//通过插槽获取该能力规格
				FGameplayAbilitySpec* SpecWithSlot = GetSpecWithSlot(Slot);
				if (SpecWithSlot)
				{
					//匹配新能力标签 和 当前到位的能力标签是否一致，如果相同 直接返回
					if (AbilityTag.MatchesTagExact(GetAbilityTagFromSpec(*SpecWithSlot)))
					{
						//通知客户端完成装备技能后续操作
						ClientEquipAbility(AbilityTag, GameplayTags.Abilities_Status_Equipped, Slot, PrevSlot);
						return;
					}
				}
				//判断能力类型 是不是被动
				if (IsPassiveAbility(*SpecWithSlot))
				{
					//广播停用被动能力
					DeactivatePassiveAbilityDelegate.Broadcast(GetAbilityTagFromSpec(*SpecWithSlot));
					//广播停用被动效果组件
					MulticastActivatePassiveEffect(GetAbilityTagFromSpec(*SpecWithSlot), false);
				}
				//清除当前槽位
				ClearSlot(SpecWithSlot);
			}
			//判断新能力是否有任何一个输入槽位，如果有则需要清理之前的，如果没有则不需要（有槽位 又没有按键 说明是被动）
			if (!AbilityHasAnySlot(*AbilitySpec))
			{
				//判断能力类型 是不是被动
				if (IsPassiveAbility(*AbilitySpec))
				{
					//尝试激活
					TryActivateAbility(AbilitySpec->Handle);
					//广播停用被动效果组件
					MulticastActivatePassiveEffect(AbilityTag, true);
				}
				//先移除该能力旧状态标签(这个只是为了安全考虑）
				AbilitySpec->DynamicAbilityTags.RemoveTag(GetStatusFromSpec(*AbilitySpec));
				//给能力添加已装备标签 解决读取存档时无法激活已装备的被动能力问题
				AbilitySpec->DynamicAbilityTags.AddTag(GameplayTags.Abilities_Status_Equipped);
			}
			//给新能力分配槽位，如果新能力分配过其他槽位则清理掉其他槽位再分配新的
			AssignSlotToAbility(*AbilitySpec, Slot);
			/* 旧版写法 注释 主动技能部分
			//移除所有已激活技能中的 输入槽位标签
			ClearAbilitiesOfSlot(Slot);
			//清除当前槽位
			ClearSlot(AbilitySpec);
			//分配新技能到这个槽位，也就是把这个槽位的输入标签分配到新技能能力上
			AbilitySpec->DynamicAbilityTags.AddTag(Slot);
			//如果是已解锁状态，则改为已装备状态
			if (Status.MatchesTagExact(GameplayTags.Abilities_Status_Unlocked))
			{
				AbilitySpec->DynamicAbilityTags.RemoveTag(GameplayTags.Abilities_Status_Unlocked);
				AbilitySpec->DynamicAbilityTags.AddTag(GameplayTags.Abilities_Status_Equipped);
			}
			*/
			//立即更新技能能力
			MarkAbilitySpecDirty(*AbilitySpec);
		}
		ClientEquipAbility(AbilityTag, GameplayTags.Abilities_Status_Equipped, Slot, PrevSlot);
	}	
}
//广播给客户端装备技能, 能力标签、状态标签、按键输入插槽标签、上一个输入插槽标签
void UBaseAbilitySystemComponent::ClientEquipAbility_Implementation(const FGameplayTag& AbilityTag, const FGameplayTag& Status, const FGameplayTag& Slot, const FGameplayTag& PreviousSlot)
{
	AbilityEquippedDelegate.Broadcast(AbilityTag, Status, Slot, PreviousSlot);
}

//清空技能插槽，用于装备技能或脱出技能时 清空上一个或当前技能插槽
void UBaseAbilitySystemComponent::ClearSlot(FGameplayAbilitySpec* Spec)
{
	const FGameplayTag Slot = GetInputTagFromSpec(*Spec);
	//移除当前技能的输入标签，这样就可以清空掉插槽了
	Spec->DynamicAbilityTags.RemoveTag(Slot);
	//立即更新技能
	// MarkAbilitySpecDirty(*Spec);
}
//清空掉所有已激活能力中对应的插槽标签
void UBaseAbilitySystemComponent::ClearAbilitiesOfSlot(const FGameplayTag& Slot)
{
	//创建一个已激活的能力列表锁
	FScopedAbilityListLock ActiveScopeLock(*this);
	//遍历所有已激活的能力
	for (FGameplayAbilitySpec& Spec : GetActivatableAbilities())
	{
		//判断是否有该槽位输入标签
		if (AbilityHasSlot(&Spec, Slot))
		{
			//如果有就清除掉输入标签，即可做到清除技能插槽
			ClearSlot(&Spec);
		}
	}
}
//判断是否有该槽位标签
bool UBaseAbilitySystemComponent::AbilityHasSlot(FGameplayAbilitySpec* Spec, const FGameplayTag& Slot)
{
	//遍历能力规格的所有动态标签
	for (FGameplayTag Tag: Spec->DynamicAbilityTags)
	{
		//检查是否有对应的插槽输入标签
		if (Tag.MatchesTagExact(Slot))
		{
			return true;
		}
	}
	return false;
}

//通过能力标签 获取能力描述
bool UBaseAbilitySystemComponent::GetDescriptionsByAbilityTag(const FGameplayTag& AbilityTag, FString& OutDescription, FString& OutNextLevelDescription)
{
    // 直接从 AbilityInfo 读取模板并传递给能力，能力内部会在模板为空时回退到默认描述
    if (const UAbilityInfo* AbilityInfo =  UBaseAbilitySystemLibrary::GetAbilityInfo(GetAvatarActor()))
    {
    	if (!AbilityTag.IsValid() || AbilityTag.MatchesTagExact(FBaseGameplayTags::Get().Abilities_None))
    	{
    		OutDescription = FString();
    		return false;
    	}
        const FBaseAbilityInfo Info = AbilityInfo->FindAbilityInfoForTag(AbilityTag, false);
        const FText& Title = Info.Title;
        const FText& Description = Info.Description;

        //通过能力标签获取 能力规格
        if (const FGameplayAbilitySpec* AbilitySpec = GetSpecFromAbilityTag(AbilityTag))
        {
            //取出并转换游戏能力
            if (UBaseGameplayAbility* BaseAbility = Cast<UBaseGameplayAbility>(AbilitySpec->Ability.Get()))
            {
                // 当前等级描述（若模板为空，由能力内部回退默认值）
                OutDescription = BaseAbility->GetDescription(AbilitySpec->Level, Title, Description);
                // 下一等级描述（若模板为空，由能力内部回退默认值）
                OutNextLevelDescription = BaseAbility->GetNextLevelDescription(AbilitySpec->Level + 1, Title, Description);
                return true;
            }
        }

        // 如果没找到对应已激活的能力，则返回锁定描述
        OutDescription = UBaseGameplayAbility::GetLockedDescription(Info.LevelRequirement);
        OutNextLevelDescription = FString();
    }

    return false;
}

//多播激活被动能力粒子效果函数，用于广播被动能力激活粒子效果
void UBaseAbilitySystemComponent::MulticastActivatePassiveEffect_Implementation(const FGameplayTag& AbilityTag, bool bActivate)
{
	//广播激活被动技能粒子效果
	ActivatePassiveEffectDelegate.Broadcast(AbilityTag, bActivate);
}

//重写激活能力函数，用于复制给客户端激活能力
void UBaseAbilitySystemComponent::OnRep_ActivateAbilities()
{
	Super::OnRep_ActivateAbilities();
	if (!bStartupAbilitiesGiven)
	{
		bStartupAbilitiesGiven = true;
		//在这里广播已激活能力信息可以复制给客户都安
		AbilitiesGivenDelegate.Broadcast();
	}
}
//客户端更新技能能力状态
void UBaseAbilitySystemComponent::ClientUpdateAbilityStatus_Implementation(const FGameplayTag& AttributeTag, const FGameplayTag& InputTag, const FGameplayTag& StatusTag, int32 AbilityLevel)
{
	AbilityStatusChangedDelegate.Broadcast(AttributeTag, InputTag, StatusTag,AbilityLevel);
}

/**
 * 当GameplayEffect被应用到当前AbilitySystemComponent时调用的回调函数
 * @param AbilitySystemComponent 应用该效果的能力系统组件
 * @param EffectSpec 包含效果规格信息的结构体，包括效果等级、持续时间等参数
 * @param ActiveEffectHandle 该激活效果的处理，可用于后续查询或操作该效果
 * 设置成RPC 客户端复制后 必须要带_Implementation
 */
void UBaseAbilitySystemComponent::ClientEffectApplied_Implementation(UAbilitySystemComponent* AbilitySystemComponent,
												const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle)
{
	//当效果物品使用时会触发这个方法
	//通过获取游戏玩法标签，来广播到对应的小组件控制器，然后由小部件控制器广播到HUD游戏界面中
	//标签容器
	FGameplayTagContainer TagContainer;
	//获取资产游戏玩法标签 存储到标签容器中
	EffectSpec.GetAllAssetTags(TagContainer);
	//广播游戏资产标签触发
	EffectAssetTags.Broadcast(TagContainer);
}


